// Copyright (C) 2012 WHTY
package com.whty.system.util;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Created by IntelliJ IDEA. User: fibiger Date: 2009-05-05 Time: 10:18:48 To
 * change this template use File | Settings | File Templates.
 */
public class GPMethods
{

  private static final Logger                logger            = LoggerFactory.getLogger(GPMethods.class);
  /** 保存卡的临时会话信息 ，key:手机号码 value:会话信息 */

  private static String                      ip                ;//= PropertiesHolder.getProperty("DSP_IP");  // "10.8.15.200";
  private static String                      port             ; //= PropertiesHolder.getProperty("DSP_PORT"); // "12002";

  // private static String ip = "10.8.15.200";
  // private static String port = "12002";

  // public static String ip = "10.8.15.200";
  // public static String port = "12002";
  /**
   * 按位异或byte数组 (两个byte数组的长度必须一样)
   * 
   * @param b1
   * @param b2
   * @return
   */
  public static String doXOR(String b1, String b2)
  {
    if (b1.length() != b2.length())
    {
      return null;
    }
    byte[] byte1 = Converts.stringToBytes(b1);
    byte[] byte2 = Converts.stringToBytes(b2);

    byte[] result = new byte[byte1.length];
    for (int i = 0; i < byte1.length; i++)
    {
      int temp = (byte1[i] ^ byte2[i]) & 0xff;
      result[i] = (byte) temp;
    }
    return Converts.bytesToHex(result).toUpperCase();
  }

  public static String padding(String data1, String pad)
  {
    String data = data1;
    int padlen = 8 - (data.length() / 2) % 8;
    if (padlen != 8)
    {
      String padstr = "";
      for (int i = 0; i < padlen; i++)
      {
        padstr += pad;
      }
      data += padstr;
      return data;
    } else
    {
      return data;
    }
  }

  /**
   * 填充05数据，如果结果数据块是8的倍数，不再进行追加,如果不是,追加0x05到数据块的右边，直到数据块的长度是8的倍数。
   * 
   * @param data
   *          待填充05的数据
   * @return
   */
  public static String padding05(String data1)
  {
    // String data = data1;
    // int padlen = 8 - (data.length() / 2) % 8;
    // if (padlen != 8) {
    // String padstr = "";
    // for (int i = 0; i < padlen; i++) {
    // padstr += "05";
    // }
    // data += padstr;
    // return data;
    // }
    // else {
    // return data;
    // }
    return padding(data1, "05");
  }

  /**
   * 填充00数据，如果结果数据块是8的倍数，不再进行追加,如果不是,追加0x00到数据块的右边，直到数据块的长度是8的倍数。
   * 
   * @param data
   *          待填充00的数据
   * @return
   */
  public static String padding00(String data1)
  {
    // String data = data1;
    // int padlen = 8 - (data.length() / 2) % 8;
    // if (padlen != 8) {
    // String padstr = "";
    // for (int i = 0; i < padlen; i++) {
    // padstr += "00";
    // }
    // data += padstr;
    // return data;
    // }
    // else {
    // return data;
    // }
    return padding(data1, "00");
  }

  /**
   * 填充20数据，如果结果数据块是8的倍数，不再进行追加,如果不是,追加0x20到数据块的右边，直到数据块的长度是8的倍数。
   * 
   * @param data
   *          待填充20的数据
   * @return
   */
  public static String padding20(String data1)
  {
    // String data = data1;
    // int padlen = 8 - (data.length() / 2) % 8;
    // if (padlen != 8) {
    // String padstr = "";
    // for (int i = 0; i < padlen; i++) {
    // padstr += "20";
    // }
    // data += padstr;
    // return data;
    // }
    // else {
    // return data;
    // }
    return padding(data1, "20");
  }

  /**
   * 填充80数据，首先在数据块的右边追加一个
   * '80',如果结果数据块是8的倍数，不再进行追加,如果不是,追加0x00到数据块的右边，直到数据块的长度是8的倍数。
   * 
   * @param data
   *          待填充80的数据
   * @return
   */
  public static String padding80(String data1)
  {
    String data = data1;
    int padlen = 8 - (data.length() / 2) % 8;
    String padstr = "";
    for (int i = 0; i < padlen - 1; i++)
    {
      padstr += "00";
    }
    data = data + "80" + padstr;
    return data;
  }

 


  
  public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException
{
//    String data="81B701312e3131000000911200000000020000183030011839393132323130393331323130363035373730323230313430353233313631313533464646464646464646464646464646464646464620202020202020202020202031323334353637383337333135303431353932453533353935333245343434343436333020202020312e31313030303030303030540017000100A4040010D1560001010001600000000100000000000F000280502000089C5D835919C4C260";
//    byte[] byteData=Converts.stringToBytes(data);
//    Cipher enc = Cipher.getInstance("DESede/ECB/NoPadding");
//    String key="C9562651DA296BA8797C2580825C4D8A";
//    SecretKeySpec keySpec = new SecretKeySpec(Converts.stringToBytes(key + key.substring(0, 16)), "DESede");
//    enc.init(0, keySpec);
//    data=Converts.bytesToHex(enc.doFinal(byteData));
////    System.err.println();
//		String  yd_em_ip=Global.getConfig("yd_em_ip");
//		int  yd_em_port=Integer.parseInt(Global.getConfig("yd_em_port"));  
//		String msg="123456785IK0012D15600010101017200000000B00143011800000903300289000903300289305C00000000000000000181000000000000000000000000000001010016EBE5836DB7E865176FACC784C1F46FB8";
//	    String res=new SimpleMessageTcpClientHelper().sendMsgByLen(yd_em_ip, yd_em_port, msg.length(),msg.getBytes());
//		res=res.substring(14);
//		System.out.println(res);
	String s=  generateCRC32(CommonMethods.stringToBytes("313233343536373839"),CommonMethods.stringToBytes("00000000"),1);
	System.out.println(s);
}

  // /**
  // * ECB模式中的DES/3DES/TDES算法(数据需要填充80),支持8、16、24字节的密钥 说明：3DES/TDES解密算法 等同与
  // * 先用前8个字节密钥DES解密 再用中间8个字节密钥DES加密 最后用后8个字节密钥DES解密 一般前8个字节密钥和后8个字节密钥相同
  // *
  // * @param key 加解密密钥(8字节:DES算法 16字节:3DES/TDES算法
  // * 24个字节:3DES/TDES算法,一般前8个字节密钥和后8个字节密钥相同)
  // * @param data 待加/解密数据
  // * @param mode 0-加密，1-解密
  // * @return 加解密后的数据 为null表示操作失败
  // */
  // public static String desecbNeedPadding80(String key, String data, int
  // mode)
  // {
  // return desecb(key, padding80(data), mode);
  // }
  //
  


  


  /**
   * SHA-1摘要 应用场景 算install指令中的HASH值
   * 
   * @param data
   *          要计算SHA-1摘要的数据
   * @return 16进制字符串形式的SHA-1消息摘要，一般为20字节 为null表示操作失败
   */
  public static String generateSHA1(String data)
  {
    try
    {
      // 使用getInstance("算法")来获得消息摘要,这里使用SHA-1的160位算法
      MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");

      // 开始使用算法
      messageDigest.update(Converts.stringToBytes(data));

      // 输出算法运算结果
      byte[] hashValue = messageDigest.digest(); // 20位字节

      return (Converts.bytesToHex(hashValue)).toUpperCase();
    } catch (Exception e)
    {
      logger.debug(e.getMessage());
    }
    return null;
  }


  


  // /**
  // * 将整数转为16进行数后并以指定长度返回（当实际长度大于指定长度时只返回从末位开始指定长度的值）
  // *
  // * @param val
  // * 待转换整数
  // * @param len
  // * 指定长度
  // * @return String
  // */
  // public static String int2HexStr(int val, int len) {
  // String result = Integer.toHexString(val).toUpperCase(); // EEEEEEEEE
  // int rLen = result.length();
  // if (rLen > len) {
  // return result.substring(rLen - len, rLen);
  // }
  // if (rLen == len) {
  // return result;
  // }
  // StringBuffer strBuff = new StringBuffer(result);
  // for (int i = 0; i < len - rLen; i++) {
  // strBuff.insert(0, '0');
  // }
  // return strBuff.toString();
  // }

  // *********************************2012-06-06新增的方法******************
  public static String getStatus(int str, String seid)
  {

    return "";
  }

  



 
  /**
   * 生成带SHA-1摘要的RSA签名
   * 
   * @param N
   *          RSA的模modulus
   * @param D
   *          RSA私钥的指数exponent
   * @param data
   *          输入数据
   * @return 签名后的数据 为null表示操作失败
   */
  public static String generateSHA1withRSA(String n, String d, String data)
  {
    try
    {
      KeyFactory keyFactory = KeyFactory.getInstance("RSA");
      BigInteger bigN = new BigInteger(n);
      BigInteger bigD = new BigInteger(d);
      KeySpec keySpec = new RSAPrivateKeySpec(bigN, bigD);
      PrivateKey key = keyFactory.generatePrivate(keySpec);

      // 使用私钥签名
      Signature sig = Signature.getInstance("SHA1WithRSA"); // SHA1WithRSA
      sig.initSign(key);
      sig.update(Converts.stringToBytes(data));

      // 返回加密结果
      return (Converts.bytesToHex(sig.sign())).toUpperCase(); // 开始计算
    } catch (Exception e)
    {
      e.printStackTrace();
      logger.debug(e.getMessage());
    }
    return null;
  }

  /**
   * 生成带SHA-1摘要的RSA签名
   * 
   * @param key
   *          DAP或者Token私钥 用PKCS#8编码
   * @param data
   *          要签名的原始数据
   * @return 签名后的数据 为null表示操作失败
   */
  public static String generateSHA1withRSA(String key, String data) throws Exception
  {

    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    KeySpec keySpec = new PKCS8EncodedKeySpec(Converts.stringToBytes(key));
    // PKCS#8编码
    // System.out.println(keySpec);
    PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

    // 使用私钥签名
    Signature sig = Signature.getInstance("SHA1WithRSA"); // SHA1WithRSA
    sig.initSign(privateKey);
    sig.update(Converts.stringToBytes(data));

    // 返回加密结果
    return (Converts.bytesToHex(sig.sign())).toUpperCase(); // 开始计算

  }

  /**
   * 填充FF数据，如果结果数据块是8的倍数，不再进行追加,如果不是,追加0xFF到数据块的第2个字节后，直到数据块的长度是8的倍数。
   * 
   * @param data
   *          待填充FF的数据
   * @return
   */
  public static String paddingFF(String data1)
  {
    String data = data1;
    int padlen = 8 - (data.length() / 2) % 8;
    if (padlen != 8)
    {
      String padstr = "";
      for (int i = 0; i < padlen; i++)
      {
        padstr += "FF";
      }
      data = data.substring(0, 4) + padstr + data.substring(4);
      return data;
    } else
    {
      return data;
    }
  }

 

  /**
   * 
   * @param data
   * @return o[0] 数据域；o[1] 状态域;o[2] 失败的指令（指令长度2字节+指令+期望响应码长度2字节+期望响应码）
   */
  public static String[] getResponseAndApdu(String data)
  {
    String[] o = new String[3];
    if (data != null && data.length() >= 4)
    {
      int responseSplit = 4 + Integer.parseInt(data.substring(0, 4), 16) * 2;
      String response = data.substring(4, responseSplit);
      int responseLen = response.length();
      if (response != null && responseLen >= 4)
      {
        o[0] = response.substring(0, responseLen - 4);
        o[1] = response.substring(responseLen - 4);
      } else
      {
        o[0] = null;
        o[1] = null;
      }
      o[2] = data.substring(responseSplit);
    } else
    {
      o[0] = null;
      o[1] = null;
      o[2] = null;
    }

    return o;
  }

  // /**
  // * 将16进制组成的字符串转换成byte数组 例如 hex2Byte("0710BE8716FB"); 将返回一个byte数组
  // * b[0]=0x07;b[1]=0x10;...b[5]=0xFB;
  // *
  // * @param src
  // * 待转换的16进制字符串
  // * @return
  // */
  // public static byte[] str2bytes(String src) {
  // if (src == null || src.length() == 0 || src.length() % 2 != 0) {
  // return null;
  // }
  // int nSrcLen = src.length();
  // byte byteArrayResult[] = new byte[nSrcLen / 2];
  // StringBuffer strBufTemp = new StringBuffer(src);
  // String strTemp;
  // int i = 0;
  // while (i < strBufTemp.length() - 1) {
  // strTemp = src.substring(i, i + 2);
  // byteArrayResult[i / 2] = (byte) Integer.parseInt(strTemp, 16);
  // i += 2;
  // }
  // return byteArrayResult;
  // }

  /**
   * 生成CRC32
   * 
   * @param data
   *          待处理数据
   * @param strinitcrc
   *          长度必须为8
   * @param lastblock
   *          1:最后取反
   * @return 8个字节的CRC
   */
  public static String generateCRC32(byte[] data, byte[] strinitcrc, int lastblock)
  {
    long crc32 = unsigned4BytesToInt(strinitcrc);
    for (int i = 0; i < data.length; i++)
    {
      crc32 = lGenCRC32(crc32, data[i]);
    }
    if (lastblock == 1)
    {
      crc32 = ~crc32;
    }
    return long2HexStr(crc32, 8);
  }

  /**
   * 将无符号的4个字节的byte数据转换成32bit的长整形
   * 
   * @param buf
   *          无符号的4个字节数据
   * @return 32bit的长整形
   */
  public static long unsigned4BytesToInt(byte[] buf)
  {
    int firstByte = 0;
    int secondByte = 0;
    int thirdByte = 0;
    int fourthByte = 0;
    firstByte = (0x000000FF & ((int) buf[0]));
    secondByte = (0x000000FF & ((int) buf[1]));
    thirdByte = (0x000000FF & ((int) buf[2]));
    fourthByte = (0x000000FF & ((int) buf[3]));
    return ((long) (firstByte << 24 | secondByte << 16 | thirdByte << 8 | fourthByte)) & 0xFFFFFFFFL;
  }

 
  // /**
  // * 将byte数组转换成16进制组成的字符串 例如 一个byte数组 b[0]=0x07;b[1]=0x10;...b[5]=0xFB;
  // * byte2hex(b); 将返回一个字符串"0710BE8716FB"
  // *
  // * @param bytes
  // * 待转换的byte数组
  // * @return
  // */
  // public static String bytesToHexString(byte[] bytes) {
  // if (bytes == null) {
  // return "";
  // }
  // StringBuffer buff = new StringBuffer();
  // int len = bytes.length;
  // for (int j = 0; j < len; j++) {
  // if ((bytes[j] & 0xff) < 16) {
  // buff.append('0');
  // }
  // buff.append(Integer.toHexString(bytes[j] & 0xff));
  // }
  // return buff.toString();
  // }

  /**
   * 生成CRC32
   * 
   * @param lOldCRC
   *          the crc32 value
   * @param ByteVal
   *          the new data byte
   * @return
   */
  public static long lGenCRC32(long lOldCRC, byte byteVal)
  {
    long tabVal;
    int j;
    tabVal = ((lOldCRC) ^ byteVal) & 0xff;
    for (j = 8; j > 0; j--)
    {
      if ((tabVal & 1) == 1)
      {
        tabVal = (tabVal >> 1) ^ 0xEDB88320L;
      } else
      {
        tabVal >>= 1;
      }
    }
    return tabVal ^ (((lOldCRC) >> 8) & 0x00FFFFFFL);
  }

  /**
   * 将长整数转为16进行数后并以指定长度返回（当实际长度大于指定长度时只返回从末位开始指定长度的值）
   * 
   * @param val
   *          待转换长整数
   * @param len
   *          指定长度
   * @return String
   */
  public static String long2HexStr(long val, int len)
  {
    String result = Long.toHexString(val).toUpperCase();
    int rLen = result.length();
    if (rLen > len)
    {
      return result.substring(rLen - len, rLen);
    }
    if (rLen == len)
    {
      return result;
    }
    StringBuffer strBuff = new StringBuffer(result);
    for (int i = 0; i < len - rLen; i++)
    {
      strBuff.insert(0, '0');
    }
    return strBuff.toString();
  }

  /**
   * 
   * @param hash
   * @param eLen
   * @return
   */
  public static String paddingRsaPcks(String hash, int eLen)
  {
    String ps = "FF";
    String ret = "0001";
    String t = "00" + "3021300906052b0e03021a05000414" + hash;
    int psLen = eLen - (t.length() + ret.length()) / 2;
    for (int i = 0; i < psLen; i++)
    {
      ret += ps;
    }
    ret += t;
    return ret;
  }

  /**
   * 填充00数据，如果结果数据块是8的倍数，不再进行追加,如果不是,追加0x00到数据块的右边，直到数据块的长度是8的倍数。
   * 
   * @param data
   *          待填充00的数据
   * @return
   */
  public static String padding00Left(String data1)
  {
    String data = data1;
    int padlen = 8 - (data.length() / 2) % 8;
    if (padlen != 8)
    {
      String padstr = "";
      for (int i = 0; i < padlen; i++)
      {
        padstr += "00";
      }
      data = padstr + data;
      return data;
    } else
    {
      return data;
    }
  }

 


  /**
   * 获取分散数据
   * 
   * @param seid
   * @param sdAid
   * @param keyVer
   * @param keyInx
   * @return
   */
  public static String getScatterData(String seid, String sdAid, String keyVer, String keyInx)
  {
    if (seid != null)
    {
      if (seid.length() >= 16)
      {
        // 截取
        seid = seid.substring(seid.length() - 16, seid.length());
      } else
      {
        // 填充
        seid = GPMethods.padding00Left(seid);
      }
    }

    if (sdAid != null)
    {
      if (sdAid.length() >= 16)
      {
        // 截取
        sdAid = sdAid.substring(sdAid.length() - 16, sdAid.length());
      } else
      {
        // 填充
        sdAid = GPMethods.padding00Left(sdAid);
      }
    }

    String keyInx1 = "000000000000" + keyInx;
    return seid + GPMethods.doXOR(sdAid, keyVer + keyInx1);
  }

  

  /**
   * 逆向去掉填80
   * 
   * @param data
   * @return
   */
  public static String unPadding80(String data)
  {
    int len = data.length();
    int padlen = (len / 2) % 8;
    if (padlen != 0)
    {
      return data;
    } else
    {
      String tempStr = data.substring(data.length() - 16);
      for (int i = 0; i < 8; i++)
      {
        int start = 2 * i;
        String temp = tempStr.substring(start, start + 2);
        if ("80".equals(temp))
        {
          if (i == 7)
          {
            return data.substring(0, len - 2);
          } else
          {
            int x = Integer.parseInt(tempStr.substring(start + 2), 16);
            if (x == 0)
            {
              return data.substring(0, len - 16 + start);
            }
          }
        }
      }
      return data;
    }
  }





}
